package cn.autumnorange.app.common.provider.gts;

import cn.autumnorange.app.common.dto.ResultDto;
import com.taobao.txc.common.TxcContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

/** Created by Lintuer on 2017/10/26. AOP记录用户请求信息 */
// @Aspect:作用是把当前类标识为一个切面供容器读取
//
// @Pointcut：Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分，一是表达式，二是方法签名。方法签名必须是
// public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符，因为表达式不直观，因此我们可以通过方法签名的方式为
// 此表达式命名。因此Pointcut中的方法只需要方法签名，而不需要在方法体内编写实际代码。
// @Around：环绕增强，相当于MethodInterceptor
// @AfterReturning：后置增强，相当于AfterReturningAdvice，方法正常退出时执行
// @Before：标识一个前置增强方法，相当于BeforeAdvice的功能，相似功能的还有
// @AfterThrowing：异常抛出增强，相当于ThrowsAdvice
// @After: final增强，不管是抛出异常或者正常退出都会执行

@Aspect
@Order(1)
@Component
@Slf4j
public class GtsProviderRestAspect {
  @Autowired private GtsProviderService gtsProviderServiceImpl;

  private final String ExpGetResultDataPonit =
      "execution(public * cn.autumnorange.app..provider.rest..*Rest.*(..))";

  // execution(public * cn.autumnorange.app.rest.*.*.*(..))
  // cn.autumnorange.app.rest 表示cn.autumnorange.app.rest包
  // cn.autumnorange.app.rest.* 表示cn.autumnorange.app.rest包和其下一级包
  // 定义切入点--正则表达式
  @Pointcut(ExpGetResultDataPonit)
  public void restAspect() {}

  //  @Before("restAspect()")
  //  public void doBefore(JoinPoint joinPoint) {
  //    String xid = gtsProviderServiceImpl.getXIDByHeader();
  //    if (StringUtils.isNotBlank(xid)) {
  //      log.info("xid传入执行bind xid:{}", xid);
  //      TxcContext.bind(xid, null);
  //    } else {
  //      log.info("xid没有传入不执行bind----------------");
  //    }
  //  }

  //    //    在某连接点正常完成后执行的通知，不包括抛出异常的情况。
  //    //    GTS API
  // 参见https://help.aliyun.com/document_detail/62673.html?spm=5176.11065259.1996646101.searchclickresult.2204518bcsZ1xz
  //    //    在阿里的gts样例工程中逻辑执行出错直接返回没有执行 xcContext.unbind();所以服务异常不用执行unbind 服务逻辑错误不用执行unbind
  //    @AfterReturning(value = "execution(public * cn.autumnorange.app.rest..*Rest.*(..))",
  // returning = "resultVo")
  //    public void runAfterReturning(ResultVo resultVo) {
  //        String xid = getXIDByHeader();
  //        if (StringUtils.isNotBlank(xid)) {
  //            if (resultVo.getCode() == 200) {
  //                log.info("xid：{} unbind", xid);
  //                TxcContext.unbind();
  //            }
  //        }
  //    }

  // 当某连接点退出的时候执行的通知（不论是正常返回还是异常退出）。
  //    public * cn.autumnorange.app.rest..*Rest.*(..))"
  //    ..表示子包 app..rest..*Rest.*(..))表示 app包及其子包下包含的provider.rest包及其子包的后缀为Rest的类的任意入参数的所有方法
  //  @After(value = "execution(public * cn.autumnorange.app..provider.rest..*Rest.*(..))")
  //  public void runAfter() {
  //    String xid = gtsProviderServiceImpl.getXIDByHeader();
  //    if (StringUtils.isNotBlank(xid)) {
  //      log.info("unbind xid:{} ", xid);
  //      TxcContext.unbind();
  //    } else {
  //      log.info("xid没有传入不执行ubindubindubind----------------");
  //    }
  //  }

  // springboot的注解默认的扫描范围为SpringApplication启动类所处包及其子包
  // aop切面没成功的一个原因是启动类在切面类的子包中或者不在同一集合包中，如cn.SpringApplication cm.Aspect不是同一集合包

  @Around(ExpGetResultDataPonit)
  public Object around(ProceedingJoinPoint point) throws Exception {
    Object result = null;
    Exception exception = null;
    try {
      // 分布式事务流程id
      String xid = gtsProviderServiceImpl.getXIDByHeader();
      if (StringUtils.isNotBlank(xid)) {
        log.info("生产端线程:" + Thread.currentThread().getId() + " bind绑定分布式事务xid:{}", xid);
        // 绑定分布式事务id
        TxcContext.bind(xid, null);
      }
      //      切面介入工作....前置通知 消费端
      Object[] args = point.getArgs(); // 获取目标方法的所有参数的值
      // 目标方法真正执行...
      result = point.proceed(args);
      // todo 根据你的实际需要，做真正的处理
      //      log.info("切面介入工作....返回通知");
      if (StringUtils.isNotBlank(xid)) {
        TxcContext.unbind();
        log.info("生产端线程:" + Thread.currentThread().getId() + " unbind解除绑定分布式事务xid:{}", xid);
      }
    } catch (Exception e) {
      log.error("生产端线程:" + Thread.currentThread().getId() + " 异常:{}", e.getMessage());
      exception= e;
      //      result =
      //          ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
      //              .body(
      //                  ResultDto.createResult(
      //                      HttpStatus.INTERNAL_SERVER_ERROR.value(),
      //                      null,
      //                      e.getMessage(),
      //                      null,
      //                      null,
      //                      null));
    } finally {
      if(exception!=null){
        throw exception;
      }
      return result;
    }
  }

  public static void main(String[] args) {
    System.out.println(testFinally(0));
  }

  public static int testFinally(int result) {
    //    try{
    return result = result / result;
    //    } catch (Exception e) {
    ////      log.error("生产端线程:"+Thread.currentThread().getId()+" 异常:{}", e.getMessage());
    //      throw new RuntimeException(e);
    //    } finally {
    //      return ++result;
    //    }
  }
}
